home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume17 / remind / part02 < prev    next >
Encoding:
Internet Message Format  |  1991-02-19  |  55.2 KB

  1. From: dfs@doe.carleton.ca (David F. Skoll)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i004:  remind - A replacement for calendar, Part02/04
  4. Message-ID: <1991Feb19.162802.16192@sparky.IMD.Sterling.COM>
  5. Date: 19 Feb 91 16:28:02 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: aa12cd2e e27052be e1256319 e257cbfc
  8.  
  9. Submitted-by: David F. Skoll <dfs@doe.carleton.ca>
  10. Posting-number: Volume 17, Issue 4
  11. Archive-name: remind/part02
  12.  
  13. #! /bin/sh
  14. # This is a shell archive.  Remove anything before this line, then feed it
  15. # into a shell via "sh file" or similar.  To overwrite existing files,
  16. # type "sh file -c".
  17. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  18. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  19. # If this archive is complete, you will see the following message at the end:
  20. #        "End of archive 2 (of 4)."
  21. # Contents:  calendar.c dorem.c main.c test.out
  22. # Wrapped by kent@sparky on Tue Feb 19 10:16:40 1991
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. if test -f 'calendar.c' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'calendar.c'\"
  26. else
  27. echo shar: Extracting \"'calendar.c'\" \(13183 characters\)
  28. sed "s/^X//" >'calendar.c' <<'END_OF_FILE'
  29. X/***************************************************************/
  30. X/*                                                             */
  31. X/* CALENDAR.C                                                  */
  32. X/*                                                             */
  33. X/* Contains routines and data structures for producing a       */
  34. X/* calendar from a reminder file.                              */
  35. X/*                                                             */
  36. X/* By David Skoll - 14 November 1990                           */
  37. X/*                                                             */
  38. X/***************************************************************/
  39. X#include <stdio.h>
  40. X#ifndef NO_MALLOC_H
  41. X#include <malloc.h>
  42. X#endif
  43. X#include <ctype.h>
  44. X#include <string.h>
  45. X#ifndef UNIX
  46. X#include <stdlib.h>
  47. X#endif
  48. X#include "defines.h"
  49. X#include "globals.h"
  50. X#include "protos.h"
  51. X#include "cache.h"
  52. X
  53. X/* Convert (monday-sunday) sequence to (sunday-saturday) */
  54. X#define DayOfWeek(julian) ((((julian) % 7) + 1) % 7)
  55. X
  56. X/* To center an item of length l in a field of length f, how many spaces? */
  57. X#define PreCenter(f, l) (((f)-(l))/2)
  58. X
  59. X/* How many spaces AFTEr the centered item? */
  60. X#define PostCenter(f, l) ((f)-(l)-((f)-(l))/2)
  61. X
  62. X/* Define the structure of a calendar entry */
  63. Xtypedef struct CalEntry_t {
  64. X   int tim;
  65. X   char *text, *current;
  66. X   struct CalEntry_t *next;
  67. X} CalEntry;
  68. X
  69. X/* Have a main calendar entry for each weekday */
  70. XCalEntry entry[7];
  71. Xint used[7];  /* These hold the day of the month for corresponding
  72. X                 entry - 0 if not used */
  73. X
  74. X/* Static integers for various stuff */
  75. Xstatic int TotalWidth;
  76. X
  77. X/* Make function prototypes local - they're not used anywhere else */
  78. X#ifdef __STDC__
  79. XCalEntry *CreateCalEntry(int type);
  80. Xvoid AddCalEntry(CalEntry *e);
  81. Xvoid EmitOneCalendarLine(void);
  82. Xvoid InitCalendar(int m, int y);
  83. Xvoid FinishCalendar(void);
  84. Xvoid DoEntries(void);
  85. X#else
  86. XCalEntry *CreateCalEntry();
  87. Xvoid AddCalEntry();
  88. Xvoid EmitOneCalendarLine();
  89. Xvoid InitCalendar();
  90. Xvoid FinishCalendar();
  91. Xvoid DoEntries();
  92. X#endif
  93. X
  94. X/***************************************************************/
  95. X/*                                                             */
  96. X/* DoCalendar - main loop for the calendar command.            */
  97. X/*                                                             */
  98. X/***************************************************************/
  99. X#ifndef __STDC__
  100. Xvoid DoCalendar()
  101. X#else
  102. Xvoid DoCalendar(void)
  103. X#endif
  104. X{
  105. X   int y, m, d, init;
  106. X
  107. X   TotalWidth = 7*CalWidth + 8;
  108. X
  109. X   /* Move back until beginning of month */
  110. X   FromJulian(JulianToday, &d, &m, &y);
  111. X   JulianToday -= (d-1);
  112. X
  113. X   init = 0;
  114. X   InitCache();
  115. X   while (Calendar) {
  116. X      FromJulian(JulianToday, &d, &m, &y);
  117. X      CurDay = d;
  118. X      CurMon = m;
  119. X      CurYear = y;
  120. X      if (init == 0 || CurDay == 1) { InitCalendar(m, y); init = 1; }
  121. X      DoEntries();
  122. X      if (d == DaysInMonth(m, y)) Calendar--;
  123. X      JulianToday++;
  124. X      if (Calendar) ResetCache();
  125. X   }
  126. X   if (CurDay != DaysInMonth(CurMon, CurYear)) FinishCalendar();
  127. X   DestroyCache();
  128. X   FreeStackedOmits();
  129. X}
  130. X
  131. X/***************************************************************/
  132. X/*                                                             */
  133. X/* PrintChars:  Print n of the specified character             */
  134. X/* CopyChars:  Copy n of the character to the output buffer    */
  135. X/*                                                             */
  136. X/***************************************************************/
  137. X#ifdef __STDC__
  138. Xvoid PrintChars(int n, int c)
  139. X#else
  140. Xvoid PrintChars(n, c)
  141. Xint n, c;
  142. X#endif
  143. X{
  144. X   while(n--) putchar(c);
  145. X}
  146. X
  147. X#ifdef __STDC__
  148. Xchar *CopyChars(int n, int c, char *dst)
  149. X#else
  150. Xchar *CopyChars(n, c, dst)
  151. Xint n, c;
  152. Xchar *dst;
  153. X#endif
  154. X{
  155. X   while(n--) *dst++ = (char) c;
  156. X   return dst;
  157. X}
  158. X
  159. X/***************************************************************/
  160. X/*                                                             */
  161. X/* InitCalendar                                                */
  162. X/* Print the calendar header                                   */
  163. X/*                                                             */
  164. X/***************************************************************/
  165. X#ifdef __STDC__
  166. Xvoid InitCalendar(int m, int y)
  167. X#else
  168. Xvoid InitCalendar(m, y)
  169. Xint y, m;
  170. X#endif
  171. X{
  172. X   int i;
  173. X
  174. X   if (SimpleCalendar) return;
  175. X
  176. X   for (i=0; i<7; i++) {
  177. X      entry[i].next = NULL;
  178. X      used[i] = 0;
  179. X   }
  180. X
  181. X   /* Emit the calendar title */
  182. X   putchar('+');
  183. X   PrintChars(TotalWidth-2, '-');
  184. X   putchar('+');
  185. X   putchar('\n');
  186. X   sprintf(TmpBuf, "%s %d", MonthName[m], y);
  187. X   putchar('|');
  188. X   PrintChars(PreCenter(TotalWidth-2, strlen(TmpBuf)), ' ');
  189. X   printf("%s", TmpBuf);
  190. X   PrintChars(PostCenter(TotalWidth-2, strlen(TmpBuf)), ' ');
  191. X   putchar('|');
  192. X   putchar('\n');
  193. X   putchar('+');
  194. X   for (i=0; i<7; i++) {
  195. X      PrintChars(CalWidth, '-');
  196. X      putchar('+');
  197. X   }
  198. X   putchar('\n');
  199. X
  200. X   /* Put the weekdays in */
  201. X   /* Argh! Do sunday first, then take care of rest */
  202. X   i = 6;
  203. X   putchar('|');
  204. X   PrintChars(PreCenter(CalWidth, strlen(DayName[i])), ' ');
  205. X   printf(DayName[i]);
  206. X   PrintChars(PostCenter(CalWidth, strlen(DayName[i])), ' ');
  207. X
  208. X   for (i=0; i<6; i++) {
  209. X      putchar('|');
  210. X      PrintChars(PreCenter(CalWidth, strlen(DayName[i])), ' ');
  211. X      printf(DayName[i]);
  212. X      PrintChars(PostCenter(CalWidth, strlen(DayName[i])), ' ');
  213. X   }
  214. X   putchar('|');
  215. X   putchar('\n');
  216. X   putchar('+');
  217. X   for (i=0; i<7; i++) {
  218. X      PrintChars(CalWidth, '-');
  219. X      putchar('+');
  220. X   }
  221. X   putchar('\n');
  222. X}
  223. X
  224. X/***************************************************************/
  225. X/*                                                             */
  226. X/* FinishCalendar                                              */
  227. X/* Just print a form feed.                                     */
  228. X/*                                                             */
  229. X/***************************************************************/
  230. X#ifdef __STDC__
  231. Xvoid FinishCalendar(void)
  232. X#else
  233. Xvoid FinishCalendar()
  234. X#endif
  235. X{
  236. X   if (SimpleCalendar) return;
  237. X   putchar('\f');
  238. X}
  239. X
  240. X/***************************************************************/
  241. X/*                                                             */
  242. X/* DoEntries                                                   */
  243. X/* Create all the calendar entries for this week               */
  244. X/*                                                             */
  245. X/***************************************************************/
  246. X#ifdef __STDC__
  247. Xvoid DoEntries(void)
  248. X#else
  249. Xvoid DoEntries()
  250. X#endif
  251. X{
  252. X   int i;
  253. X   CalEntry *e;
  254. X
  255. X   while (1) {
  256. X      used[DayOfWeek(JulianToday)] = CurDay;
  257. X      if (GetLine()) break;
  258. X      i = ProcessLine();
  259. X      if (i>0) if (e = CreateCalEntry(i)) AddCalEntry(e);
  260. X   }
  261. X
  262. X   /* Now figure out if we should print the calendar */
  263. X   if ((DayOfWeek(JulianToday) == 6 ) || CurDay == DaysInMonth(CurMon, CurYear))
  264. X      EmitOneCalendarLine();
  265. X   if (CurDay == DaysInMonth(CurMon, CurYear)) FinishCalendar();
  266. X}
  267. X
  268. X/***************************************************************/
  269. X/*                                                             */
  270. X/* AddCalEntry                                                 */
  271. X/* Add a calendar entry for the appropriate weekday.           */
  272. X/*                                                             */
  273. X/***************************************************************/
  274. X#ifdef __STDC__
  275. Xvoid AddCalEntry(CalEntry *e)
  276. X#else
  277. Xvoid AddCalEntry(e)
  278. XCalEntry *e;
  279. X#endif
  280. X{
  281. X   CalEntry *curr, *prev;
  282. X
  283. X   prev = &entry[DayOfWeek(JulianToday)];
  284. X   curr = prev->next;
  285. X   while (curr) {
  286. X      if (e->tim == -1 || (e->tim >= curr->tim && curr->tim != -1)) {
  287. X         prev = curr;
  288. X         curr = prev->next;
  289. X      } else {
  290. X         prev->next = e;
  291. X     e->next    = curr;
  292. X         break;
  293. X      }
  294. X   }
  295. X   if (!curr) {
  296. X      prev->next = e;
  297. X      e->next = NULL;
  298. X   }
  299. X}
  300. X
  301. X#ifdef __STDC__
  302. Xvoid CopyWord(char **src, char **dst, int l)
  303. X#else
  304. Xvoid CopyWord(src, dst, l)
  305. Xchar **src, **dst;
  306. Xint l;
  307. X#endif
  308. X{
  309. X   while(**src && !isspace(**src) && l--) *(*dst)++ = *(*src)++;
  310. X}
  311. X
  312. X#ifdef __STDC__
  313. Xint WordLen(char *src)
  314. X#else
  315. Xint WordLen(src)
  316. Xchar *src;
  317. X#endif
  318. X{
  319. X   int len = 0;
  320. X   while (*src && !isspace(*src)) { len++; src++; }
  321. X   return len;
  322. X}
  323. X/***************************************************************/
  324. X/*                                                             */
  325. X/* CreateCalEntry                                              */
  326. X/*                                                             */
  327. X/* Allocates and creates a calendar entry.                     */
  328. X/*                                                             */
  329. X/* Type = 1: MSG  type = 2: RUN                                */
  330. X/*                                                             */
  331. X/*                                                             */
  332. X/***************************************************************/
  333. X#ifdef __STDC__
  334. XCalEntry *CreateCalEntry(int type)
  335. X#else
  336. XCalEntry *CreateCalEntry(type)
  337. Xint type;
  338. X#endif
  339. X{
  340. X   CalEntry *e;
  341. X   char *s, *t;
  342. X   int column, l;
  343. X   enum Token_t tok;
  344. X   
  345. X   if (type == 1) tok = Msg_t; else tok = Run_t;
  346. X   
  347. X   if (!SimpleCalendar) {
  348. X      e = (CalEntry *) malloc(sizeof(CalEntry));
  349. X
  350. X      if (e == NULL) {
  351. X         fprintf(stderr, "remind: Can't malloc to create calendar entry.\n");
  352. X         exit(1);
  353. X      }
  354. X      e->next = NULL;
  355. X      e->tim  = CalTime;
  356. X   }
  357. X
  358. X   DoSubst(WorkBuf, TmpBuf, CurDay, CurMon, CurYear, JulianToday, tok, CalTime, 1);
  359. X
  360. X   /* If the buffer contains zero-length string; forget it. */
  361. X   if (!*TmpBuf) {
  362. X      if (!SimpleCalendar) free(e);
  363. X      return (NULL);
  364. X   }
  365. X
  366. X   /* If we're doing a simple calendar, just spit out the text and end */
  367. X   if (SimpleCalendar) {
  368. X      printf("%04d/%02d/%02d: ", CurYear, 1+CurMon, CurDay);
  369. X      Output(TmpBuf);
  370. X      return (NULL);
  371. X   }
  372. X
  373. X   /* Now copy from TmpBuf to WorkBuf, splitting words as needed */
  374. X   s = TmpBuf;
  375. X   t = WorkBuf;
  376. X   column = 0;
  377. X   while (*s) {
  378. X      l = WordLen(s);
  379. X      if (column == 0 && l >= CalWidth) {
  380. X         CopyWord(&s, &t, CalWidth);
  381. X         *t++ = '\n';
  382. X         while (isspace(*s)) s++;
  383. X      }
  384. X      else if (column != 0 && column+l > CalWidth) {
  385. X         *t++ = '\n';
  386. X         column = 0;
  387. X         if (l >= CalWidth) {
  388. X            CopyWord(&s, &t, CalWidth);
  389. X            *t++ = '\n';
  390. X            while (isspace(*s)) s++;
  391. X         } else {
  392. X            CopyWord(&s, &t, l);
  393. X            *t++ = ' ';
  394. X            while (isspace(*s)) s++;
  395. X            column = l+1;
  396. X            if (column >= CalWidth) {
  397. X              *(t-1) = '\n';
  398. X              column = 0;
  399. X            }
  400. X
  401. X         }
  402. X      }
  403. X      else {
  404. X         column += l+1;
  405. X     CopyWord(&s, &t, l);
  406. X         while (isspace(*s)) s++;
  407. X         *t++ = ' ';
  408. X     if (column > CalWidth) {
  409. X           *(t-1) = '\n';
  410. X           column = 0;
  411. X         }
  412. X      }
  413. X   }
  414. X   *t = 0;
  415. X   if (*(t-1) == '\n') *(t-1) = 0;
  416. X
  417. X   /* Finally, copy the string to the calendar entry */
  418. X   e->text = (char *) malloc(strlen(WorkBuf)+1);
  419. X
  420. X   if (!e->text) {
  421. X      fprintf(stderr, "remind: Can't malloc memory for calendar text!\n");
  422. X      exit(1);
  423. X   }
  424. X   strcpy(e->text, WorkBuf);
  425. X   e->current = e->text;
  426. X
  427. X   return e;
  428. X}
  429. X
  430. X/***************************************************************/
  431. X/*                                                             */
  432. X/* EmitOneCalendarLine                                         */
  433. X/* This is the biggie - we print out one line.                 */
  434. X/*                                                             */
  435. X/***************************************************************/
  436. X#ifdef __STDC__
  437. Xvoid EmitOneCalendarLine(void)
  438. X#else
  439. Xvoid EmitOneCalendarLine()
  440. X#endif
  441. X{
  442. X   int i, nlines, emit, j;
  443. X   char *s, *dst;
  444. X   CalEntry *e;
  445. X   char pend[7];  /* Reminders with following blanks pending */
  446. X
  447. X   if (SimpleCalendar) return;
  448. X
  449. X   nlines = 0;
  450. X   for (i=0; i<7; i++) pend[i] = 0;
  451. X   putchar('|');
  452. X   /* First, emit the days of the month */
  453. X   for (i=0; i<7; i++) {
  454. X      if (!used[i]) PrintChars(CalWidth, ' ');
  455. X      else {
  456. X         sprintf(TmpBuf, "%d", used[i]);
  457. X         printf(TmpBuf);
  458. X         PrintChars(CalWidth-strlen(TmpBuf), ' ');
  459. X      }
  460. X      putchar('|');
  461. X   }
  462. X   putchar('\n');
  463. X
  464. X   /* Now cycle through all the reminders until there are none left */
  465. X   emit = 1;
  466. X   while(emit) {
  467. X      dst = WorkBuf;
  468. X      *dst++ = '|';
  469. X      emit = 0;
  470. X      for (i=0; i<7; i++) {
  471. X         if (pend[i] || !used[i] || !entry[i].next) {
  472. X            dst = CopyChars(CalWidth, ' ', dst);
  473. X        *dst++ = '|';
  474. X        if(pend[i]) {pend[i] = 0; emit = 1;}
  475. X            continue;
  476. X         }
  477. X         s = entry[i].next->current;
  478. X     j = 0;
  479. X     emit = 1;
  480. X     while (*s && *s != '\n') {
  481. X        *dst++ = *s++;
  482. X        j++;
  483. X     }
  484. X     dst = CopyChars(CalWidth - j, ' ', dst);
  485. X         if (*s == '\n') entry[i].next->current = s+1;
  486. X         else {
  487. X            e = entry[i].next;
  488. X        entry[i].next = e->next;
  489. X            free(e->text);
  490. X            free(e);
  491. X        if (!entry[i].next) used[i] = 0;
  492. X        else pend[i] = 1;
  493. X         }
  494. X         *dst++ = '|';
  495. X      }
  496. X      *dst = 0;
  497. X      if(emit) printf("%s\n", WorkBuf);
  498. X      nlines += emit;
  499. X   }
  500. X   while(nlines++ < 6) printf("%s\n", WorkBuf);
  501. X
  502. X   putchar('+');
  503. X   for (i=0; i<7; i++) {
  504. X      PrintChars(CalWidth, '-');
  505. X      putchar('+');
  506. X   }
  507. X   putchar('\n');
  508. X   for (i=0; i<7; i++) used[i] = 0;
  509. X}
  510. END_OF_FILE
  511. if test 13183 -ne `wc -c <'calendar.c'`; then
  512.     echo shar: \"'calendar.c'\" unpacked with wrong size!
  513. fi
  514. # end of 'calendar.c'
  515. fi
  516. if test -f 'dorem.c' -a "${1}" != "-c" ; then 
  517.   echo shar: Will not clobber existing file \"'dorem.c'\"
  518. else
  519. echo shar: Extracting \"'dorem.c'\" \(11579 characters\)
  520. sed "s/^X//" >'dorem.c' <<'END_OF_FILE'
  521. X#include <stdio.h>
  522. X#ifndef UNIX
  523. X#include <stdlib.h>
  524. X#endif
  525. X#include <string.h>
  526. X#include <ctype.h>
  527. X#include "defines.h"
  528. X#include "globals.h"
  529. X#include "protos.h"
  530. X
  531. X/***************************************************************/
  532. X/*                                                             */
  533. X/*  int DoRem(char **s)                                        */
  534. X/*                                                             */
  535. X/*  Process a reminder.  Return 0 if reminder not emitted,     */
  536. X/*  positive if emitted, or negative if in the past and        */
  537. X/*  will never be emitted.                                     */
  538. X/*                                                             */
  539. X/***************************************************************/
  540. X#ifdef __STDC__
  541. Xint DoRem(char **s)
  542. X#else
  543. Xint DoRem(s)
  544. Xchar **s;
  545. X
  546. X#endif
  547. X{
  548. X   int d, m, y, wd, cons, delta, back, omit, done, jul, once, repeat, skip;
  549. X   int ud, um, uy; /* Date of UNTIL */
  550. X   int uj;
  551. X   int tim, tdelta, trep;
  552. X   int d2, m2, y2;
  553. X   Token tok;
  554. X   int trigger;
  555. X   char NeedNewToken;
  556. X
  557. X   uj = ud = um = uy = d = m = y = tim = tdelta = trep = -1;
  558. X   back = delta = repeat = cons = wd = omit = once = skip = 0;
  559. X
  560. X
  561. X   done = 0;
  562. X   NeedNewToken = 1;
  563. X   while (!done) {
  564. X      if (NeedNewToken) tok = ParseToken(s);
  565. X      NeedNewToken = 1;
  566. X      switch (tok.type) {
  567. X
  568. X         case Until_t:
  569. X        NeedNewToken = 0;
  570. X        while (!done) {
  571. X           tok = ParseToken(s);
  572. X           switch (tok.type) {
  573. X              case Year_t:
  574. X             if (uy != -1) {
  575. X                Eprint("Year duplicated in UNTIL.\n");
  576. X            return 0;
  577. X             }
  578. X             uy = tok.val;
  579. X             break;
  580. X
  581. X              case Month_t:
  582. X             if (um != -1) {
  583. X                Eprint("Month duplicated in UNTIL.\n");
  584. X            return 0;
  585. X             }
  586. X             um = tok.val;
  587. X             break;
  588. X
  589. X              case Day_t:
  590. X             if (ud != -1) {
  591. X                Eprint("Day duplicated in UNTIL.\n");
  592. X            return 0;
  593. X             }
  594. X             ud = tok.val;
  595. X             break;
  596. X
  597. X                  default:
  598. X             done = 1; break;
  599. X           }
  600. X        }
  601. X        if (uy == -1 || um == -1 || ud == -1) {
  602. X           Eprint("Year, month and day must all be specified after UNTIL\n");
  603. X           return 0;
  604. X            }
  605. X            if (CheckDate(ud, um, uy)) {
  606. X               Eprint("Invalid date for UNTIL.\n");
  607. X               return 0;
  608. X            }
  609. X        uj = Julian(ud, um, uy);
  610. X        done = 0; break;
  611. X
  612. X         case Omit_t:
  613. X        NeedNewToken = 0;
  614. X              while (!done) {
  615. X           tok = ParseToken(s);
  616. X           switch(tok.type) {
  617. X              case WkDay_t:
  618. X                 if (omit & (1 << tok.val)) {
  619. X                Eprint("%s duplicated.\n", tok.str);
  620. X                return 0;
  621. X                    }
  622. X                    omit |= 1 << tok.val;
  623. X                    break;
  624. X
  625. X              default: done = 1; break;
  626. X               }
  627. X              }
  628. X        done = 0;
  629. X        break;
  630. X
  631. X     case At_t:
  632. X        NeedNewToken = 0;
  633. X              while (!done) {
  634. X               tok = ParseToken(s);
  635. X               switch(tok.type) {
  636. X
  637. X                  case Time_t:
  638. X                        if (tim != -1) {
  639. X                    Eprint("Time specified twice.\n");
  640. X                       return 0;
  641. X                    }
  642. X                    else tim = tok.val;
  643. X                        break;
  644. X
  645. X                  case Repeat_t:
  646. X                        if (trep != -1) {
  647. X                        Eprint("Time repeat factor specified twice.\n");
  648. X                        return 0;
  649. X                       }
  650. X                       trep = tok.val;
  651. X                       if (trep <= 0) {
  652. X                       Eprint("Invalid value for timerepeat factor: %d\n", repeat);
  653. X                       return 0;
  654. X                       }
  655. X                       break;
  656. X
  657. X                  case Delta_t:
  658. X                 if (tdelta != -1) {
  659. X                    Eprint("Time delta specified twice.\n");
  660. X                        return 0;
  661. X                     }
  662. X                  tdelta = ABS(tok.val);
  663. X                     break;
  664. X
  665. X              default: done = 1; break;
  666. X               }
  667. X              }
  668. X        done = 0; break;
  669. X
  670. X     case Eol_t:
  671. X        Eprint("Missing MSG or RUN in reminder.\n");
  672. X        return 0;
  673. X
  674. X     case Run_t:
  675. X     case Msg_t: done = 1; break;
  676. X
  677. X     case Skip_t:
  678. X        if (skip) {
  679. X           Eprint("Can only have one of BEFORE, AFTER or SKIP.\n");
  680. X           return 0;
  681. X            }
  682. X        skip = tok.val;
  683. X        break;
  684. X
  685. X     case Unknown_t:
  686. X        Eprint("Unknown token %s in reminder.\n", tok.str);
  687. X        return 0;
  688. X
  689. X         case Repeat_t:
  690. X            if (repeat) {
  691. X               Eprint("Repeat factor specified twice.\n");
  692. X               return 0;
  693. X            }
  694. X            repeat = tok.val;
  695. X            if (repeat <= 0) {
  696. X               Eprint("Invalid value for repeat factor: %d\n", repeat);
  697. X               return 0;
  698. X            }
  699. X            break;
  700. X
  701. X     case WkDay_t:
  702. X        if (wd & (1 << tok.val)) {
  703. X           Eprint("%s duplicated.\n", tok.str);
  704. X           return 0;
  705. X        }
  706. X        wd |= 1 << tok.val;
  707. X        cons |= WKDAY_M;
  708. X        break;
  709. X
  710. X     case Year_t:
  711. X        if (y != -1) {
  712. X           Eprint("Year specified twice.\n");
  713. X           return 0;
  714. X        }
  715. X        y = tok.val;
  716. X        cons |= YEAR_M;
  717. X        break;
  718. X
  719. X     case Month_t:
  720. X        if (m != -1) {
  721. X           Eprint("Month specified twice.\n");
  722. X           return 0;
  723. X        }
  724. X        m = tok.val;
  725. X        cons |= MONTH_M;
  726. X        break;
  727. X
  728. X     case Day_t:
  729. X        if (d != -1) {
  730. X           Eprint("Day specified twice.\n");
  731. X           return 0;
  732. X        }
  733. X        d = tok.val;
  734. X        cons |= DAY_M;
  735. X        break;
  736. X
  737. X     case Delta_t:
  738. X        if (delta) {
  739. X           Eprint("Delta specified twice.\n");
  740. X           return 0;
  741. X        }
  742. X        delta = tok.val;
  743. X        break;
  744. X
  745. X     case Back_t:
  746. X        if (back) {
  747. X           Eprint("Back specified twice.\n");
  748. X           return 0;
  749. X        }
  750. X        back = tok.val;
  751. X        break;
  752. X
  753. X     case Once_t:
  754. X        if (once) {
  755. X           Eprint("ONCE specified twice.  (How's that for an error message??)\n");
  756. X           return 0;
  757. X        }
  758. X        once = 1;
  759. X        break;
  760. X
  761. X     default:
  762. X        Eprint("Can't use token %s here.\n", tok.str);
  763. X        return 0;
  764. X      }
  765. X   }
  766. X
  767. X   /* Do some sanity checking on the reminder */
  768. X   if (repeat && (d == -1 || m == -1 || y == -1)) {
  769. X      Eprint("Can't use repeat counter unless you fully specify the date.\n");
  770. X      return 0;
  771. X   }
  772. X
  773. X   if (skip && (wd & omit)) {
  774. X      Eprint("Conflict between weekday list and local OMIT\n");
  775. X      return 0;
  776. X   }
  777. X
  778. X   if (d != -1 && m != -1 && CheckDate(d, m, y)) {
  779. X      Eprint("Illegal date specification.\n");
  780. X      return 0;
  781. X   }
  782. X   if (y != -1 && (y < BASE || y > BASE + 85)) {
  783. X      Eprint("Illegal date specification.\n");
  784. X      return 0;
  785. X   }
  786. X
  787. X   /* Print some helpful stuff if debugging */
  788. X   if (Debug) {
  789. X      if (back > 7) Eprint("Warning: 'back' > 7 could slow execution severely.\n");
  790. X      if (delta > 30 && (omit || NumFullOmit || NumPartOmit))
  791. X     Eprint("Warning: 'delta' > 30 with OMITs could slow execution severely.\n");
  792. X   }
  793. X
  794. X   if (omit == 127) {
  795. X      Eprint("Can't omit every day!\n");
  796. X      return 0;
  797. X   }
  798. X
  799. X   if (IgOnce) once = 0; 
  800. X
  801. X   /* Check if we can quickly determine reminder is not to be emitted */
  802. X   if (once && !Debug && !Purge && (LastRun == JulianToday)) return 0;
  803. X
  804. X   /* Have we passed the UNTIL date ? */
  805. X   if (uj != -1 && uj < JulianToday) {
  806. X      if (Debug) Eprint("Reminder has expired.\n");
  807. X      return -1;
  808. X   }
  809. X
  810. X   jul = GetTriggerDate(d, m, y, wd, cons, back, repeat, omit, skip);
  811. X   if (uj != -1 && uj < jul) {
  812. X      if (Debug) Eprint("Reminder has expired.\n");
  813. X      return -1;
  814. X   }
  815. X
  816. X   if (Calendar) {
  817. X      if (jul == JulianToday) {
  818. X         while (isspace(**s)) (*s)++;
  819. X         strcpy(WorkBuf, *s);
  820. X         CalTime = tim;
  821. X         if (tok.type == Run_t) return 2; else return 1;
  822. X      } else return 0;
  823. X   }
  824. X
  825. X   if (jul == -1) {
  826. X      if (Debug) Eprint("Reminder has expired.\n");
  827. X      return -1;
  828. X   } else if (jul == -2) return 0;
  829. X
  830. X   FromJulian(jul, &d2, &m2, &y2);
  831. X
  832. X   /* If we're in "Next" mode, output this one in simple-calendar format */
  833. X   if (Next) {
  834. X      DoSubst(*s, WorkBuf, d2, m2, y2, jul, tok.type, tim, 1);
  835. X      if (!*WorkBuf) return 0;
  836. X      printf("%04d/%02d/%02d: ", y2, m2+1, d2);
  837. X      printf("%s\n", WorkBuf);
  838. X      return 0;
  839. X   }
  840. X       
  841. X   /* Figure out if the reminder should be triggered */
  842. X
  843. X   trigger = MoveBack(jul, delta, omit);
  844. X
  845. X   if(Debug) {
  846. X      Eprint("%sTrigger date: %s, %d %s, %d.\n", 
  847. X              (trigger <= JulianToday ? "*" : ""), DayName[jul % 7], 
  848. X          d2, MonthName[m2], y2);
  849. X      return 0;
  850. X   }
  851. X   if (Purge || (once && (LastRun == JulianToday))) return 0;
  852. X
  853. X   while (isspace(**s)) (*s)++;
  854. X
  855. X   if (trigger <= JulianToday && !(tok.type == Run_t && IgRun)) { 
  856. X      /* Trigger a reminder */
  857. X#ifdef UNIX
  858. X      if (QueueAts && (jul == JulianToday) && (tim != -1)) {
  859. X         DoAt(tim, tdelta, trep, *s, tok.type);
  860. X      }
  861. X      if (!PrintAts && (jul == JulianToday) && tim != -1) return 0;
  862. X#endif
  863. X      if (tok.type == Msg_t) {
  864. X         if (NumEmitted == 0) {
  865. X            NumEmitted++;
  866. X            DoSubst(Banner, WorkBuf, CurDay, CurMon, CurYear, 
  867. X                    JulianToday, Msg_t, (int) (SystemTime()/ 60), 0);
  868. X            printf("%s\n", WorkBuf);
  869. X         }
  870. X         DoSubst(*s, WorkBuf, d2, m2, y2, jul, tok.type, tim, 0);
  871. X         printf("%s\n", WorkBuf);
  872. X      }
  873. X      else if (tok.type == Run_t) {
  874. X         DoSubst(*s, WorkBuf, d2, m2, y2, jul, tok.type, tim, 0);
  875. X         system(WorkBuf);
  876. X      }
  877. X      else Eprint("Error: Invalid token type %d\n", tok.type);
  878. X      return 1;
  879. X   } else return 0;
  880. X
  881. X}
  882. X
  883. X/***************************************************************/
  884. X/*                                                             */
  885. X/* GetTriggerDate                                              */
  886. X/*                                                             */
  887. X/* Gets the trigger date for a reminder, returns the julian    */
  888. X/* date, or -1 if the reminder has expired.                    */
  889. X/* Returns -2 if an error occurs.                              */
  890. X/*                                                             */
  891. X/***************************************************************/
  892. X#ifdef __STDC__
  893. Xint GetTriggerDate(int d, int m, int y, int wd, int cons, int back, int repeat, int omit, int skip)
  894. X#else
  895. Xint GetTriggerDate(d, m, y, wd, cons, back, repeat, omit, skip)
  896. Xint d, m, y, wd, cons, back, repeat, omit, skip;
  897. X#endif
  898. X#define MAXATTEMPTS 25  /* Maximum number of attempts before giving up */
  899. X
  900. X{
  901. X   int i, d2, m2, y2, jul;
  902. X   int d1, m1, y1, julstart;
  903. X   int nattempts = 0;
  904. X
  905. X   julstart = JulianToday;
  906. X
  907. X   /* If we have a skip factor of 3 (AFTER), then we must back up to
  908. X      the beginning of the block of omitted days. */
  909. X   if (skip == 3) while(julstart>=1 && IsOmitted(julstart-1, omit)) julstart--;
  910. X
  911. X   FromJulian(julstart, &d1, &m1, &y1);
  912. X
  913. X   /* Make a first stab at the date */
  914. X   i = TryNextDate(&d2, &m2, &y2, d1, m1, y1, d, m, y, wd, cons, 0);
  915. X   if (!i || repeat) jul = Julian(d2, m2, y2);
  916. X
  917. X   if (!repeat && !back && !skip) {
  918. X      if (i) return -1; else return jul;
  919. X   }
  920. X
  921. X   if (i && !repeat) return -1;
  922. X
  923. X   while (nattempts++ < MAXATTEMPTS) {
  924. X      if (back) jul = MoveBack(jul, back, omit);
  925. X      if (repeat) {
  926. X         if (jul < julstart) {
  927. X        jul += ((julstart - jul) / repeat) * repeat;
  928. X        if (jul < julstart) jul += repeat;
  929. X         }
  930. X      }
  931. X      if (skip == 2)      while (IsOmitted(jul, omit)) jul--;
  932. X      else if (skip == 3) while (IsOmitted(jul, omit)) jul++;
  933. X      if ((skip == 1 && IsOmitted(jul, omit)) || jul < JulianToday) {
  934. X         if (!repeat) {
  935. X            i = TryNextDate(&d2, &m2, &y2, d2, m2, y2, d, m, y, wd, cons, 1);
  936. X            if (i) return -1;
  937. X            jul = Julian(d2, m2, y2);
  938. X         } else {
  939. X            jul += repeat;
  940. X        back = 0;  /* We've already handled the back! */
  941. X         }
  942. X      } else return jul;
  943. X   }
  944. X   Eprint("Couldn't compute a trigger date - check that date is sensible.\n");
  945. X   return -2;
  946. X}
  947. END_OF_FILE
  948. if test 11579 -ne `wc -c <'dorem.c'`; then
  949.     echo shar: \"'dorem.c'\" unpacked with wrong size!
  950. fi
  951. # end of 'dorem.c'
  952. fi
  953. if test -f 'main.c' -a "${1}" != "-c" ; then 
  954.   echo shar: Will not clobber existing file \"'main.c'\"
  955. else
  956. echo shar: Extracting \"'main.c'\" \(20411 characters\)
  957. sed "s/^X//" >'main.c' <<'END_OF_FILE'
  958. X/***************************************************************/
  959. X/*                                                             */
  960. X/*  REMIND - version 2.3                                       */
  961. X/*                                                             */
  962. X/*  By David Skoll - 11 February 1991                          */
  963. X/*                                                             */
  964. X/*  (C) 1990, 1991 by David Skoll - all rights reserved        */
  965. X/*                                                             */
  966. X/***************************************************************/
  967. X
  968. X#include <stdio.h>
  969. X#include <string.h>
  970. X#include <ctype.h>
  971. X
  972. X#ifndef UNIX
  973. X#include <stdlib.h>
  974. X#include <dos.h>
  975. X#include <stdarg.h>
  976. X#else
  977. X#include <varargs.h>
  978. X#include <sys/types.h>
  979. X#ifdef SYSV
  980. X#include <time.h>
  981. X#else
  982. X#include <sys/time.h>
  983. X#endif
  984. X#endif
  985. X
  986. X#include "defines.h"
  987. X#include "protos.h"
  988. X
  989. X
  990. X/* List of months */
  991. Xchar *MonthName[] = {
  992. X   "January", "February", "March", "April", "May", "June",
  993. X   "July", "August", "September", "October", "November", "December"
  994. X};
  995. X
  996. X/* List of weekdays */
  997. Xchar *DayName[] = {
  998. X   "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
  999. X};
  1000. X
  1001. X/* List of recognized tokens - Keep them SORTED (case insensitive.) */
  1002. X
  1003. XToken keywd[] = {
  1004. X   { "AFTER",     Skip_t,    3, 3 },
  1005. X   { "April",     Month_t,  3, 3 },
  1006. X   { "AT",        At_t,      0, 2 },
  1007. X   { "August",    Month_t,  7, 3 },
  1008. X   { "BANNER",    Banner_t, 0, 3 },
  1009. X   { "BEFORE",    Skip_t,    2, 3 },
  1010. X   { "CLEAR-OMIT-CONTEXT", Clear_t, 0, 3},
  1011. X   { "December",  Month_t,  11, 3 },
  1012. X   { "February",  Month_t,  1, 3 },
  1013. X   { "Friday",    WkDay_t,  4, 3 },
  1014. X   { "INCLUDE",   Include_t, 0, 3 },
  1015. X   { "January",   Month_t,  0, 3 },
  1016. X   { "July",      Month_t,  6, 3 },
  1017. X   { "June",      Month_t,  5, 3 },
  1018. X   { "March",     Month_t,  2, 3 },
  1019. X   { "May",       Month_t,  4, 3 },
  1020. X   { "Monday",    WkDay_t,  0, 3 },
  1021. X   { "MSG",       Msg_t,    0, 3 },
  1022. X   { "November",  Month_t,  10, 3 },
  1023. X   { "October",   Month_t,  9, 3 },
  1024. X   { "OMIT",      Omit_t,   0, 3 },
  1025. X   { "ONCE",      Once_t,   0, 3 },
  1026. X   { "POP-OMIT-CONTEXT", Pop_t, 0, 3},
  1027. X   { "PUSH-OMIT-CONTEXT", Push_t, 0, 3 },
  1028. X   { "REM",       Rem_t,    0, 3 },
  1029. X   { "RUN",       Run_t,    0, 3 },
  1030. X   { "Saturday",  WkDay_t,  5, 3 },
  1031. X   { "September", Month_t,  8, 3 },
  1032. X   { "SKIP",      Skip_t,    1, 3 },
  1033. X   { "Sunday",    WkDay_t,  6, 3 },
  1034. X   { "Thursday",  WkDay_t,  3, 3 },
  1035. X   { "Tuesday",   WkDay_t,  1, 3 },
  1036. X   { "UNTIL",      Until_t,   0, 3 },
  1037. X   { "Wednesday", WkDay_t,  2, 3 }
  1038. X};
  1039. X
  1040. X/* List of days in month - Feb MUST be 29 for CheckDate to work. */
  1041. Xint MonthDays[] = {
  1042. X   31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  1043. X};
  1044. X
  1045. X/* Index of the first day of each month.  First array is non-leap-year;
  1046. X   second is leap-year. */
  1047. Xint MonthIndex[2][12] = {
  1048. X   { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
  1049. X   { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
  1050. X};
  1051. X
  1052. X/* Global ommissions array */
  1053. Xint FullOmitArray[FOMITSIZE];
  1054. X
  1055. X/* Global partial omissions array */
  1056. Xint PartOmitArray[POMITSIZE];
  1057. X
  1058. X/* Define the working buffers */
  1059. Xchar Line[512], WorkBuf[512];
  1060. Xchar TmpBuf[512];
  1061. Xchar Fresh;  /* True if the contents of Line are fresh */
  1062. X
  1063. X/* Global variables */
  1064. Xchar Purge, Debug, Verbose, IgRun, IgOnce, Next;
  1065. Xint LastRun;
  1066. Xchar FileName[200];
  1067. Xint CurLine;
  1068. Xchar Banner[200] = "Reminders for %w, %d%s %m, %y%o:";
  1069. Xint NumEmitted, NumRem;
  1070. Xint NumFullOmit, NumPartOmit;
  1071. Xint JulianToday, RealToday;
  1072. Xint CurYear, CurMon, CurDay;
  1073. Xchar QueueAts, PrintAts;
  1074. Xint  NumAtsQueued;
  1075. Xint Calendar, CalTime, CalWidth, SimpleCalendar;
  1076. X
  1077. Xstatic int JulFirst; /* Julian date of 1 Jan Current_year */
  1078. Xstatic int FirstYear;
  1079. X
  1080. X/***************************************************************/
  1081. X/*                                                             */
  1082. X/* Output                                                      */
  1083. X/* Output a string with line separators.                       */
  1084. X/*                                                             */
  1085. X/***************************************************************/
  1086. X#ifdef __STDC__
  1087. Xvoid Output(char *s)
  1088. X#else
  1089. Xvoid Output(s)
  1090. Xchar *s;
  1091. X#endif
  1092. X{
  1093. X   while (*s) {
  1094. X      if (*s == '\n') putchar('\\');
  1095. X      putchar(*s++);
  1096. X   }
  1097. X   putchar('\n');
  1098. X}
  1099. X
  1100. X/***************************************************************/
  1101. X/*                                                             */
  1102. X/*  int MoveBack(...)                                          */
  1103. X/*                                                             */
  1104. X/*  Move back by specified number of days, skipping holidays   */
  1105. X/*  if the amound to move back is positive; otherwise, just    */
  1106. X/*  move back (-back) days, ignoring holidays.                 */
  1107. X/*                                                             */
  1108. X/***************************************************************/
  1109. X#ifdef __STDC__
  1110. Xint MoveBack (int jul, int back, int omit)
  1111. X#else
  1112. Xint MoveBack (jul, back, omit)
  1113. X     int jul;
  1114. X     int back;
  1115. X     int omit;
  1116. X#endif
  1117. X{
  1118. X   if (back <= 0) return jul+back;
  1119. X   
  1120. X   if (!NumFullOmit && !NumPartOmit && !omit) return jul - back;
  1121. X   while (back) {
  1122. X      jul--;
  1123. X      if (!IsOmitted(jul, omit)) back--;
  1124. X   }
  1125. X   return jul;
  1126. X}
  1127. X
  1128. X/***************************************************************/
  1129. X/*                                                             */
  1130. X/*  int ProcessLine()                                          */
  1131. X/*                                                             */
  1132. X/*  Process the line in the "Line" buffer.                     */
  1133. X/*                                                             */
  1134. X/*  Normally returns 0.  Returns 1 only if we're in Calendar   */
  1135. X/*  mode and we hit a reminder which should be placed in the   */
  1136. X/*  calendar.                                                  */
  1137. X/*                                                             */
  1138. X/***************************************************************/
  1139. X#ifdef __STDC__
  1140. Xint ProcessLine(void)
  1141. X#else
  1142. Xint ProcessLine()
  1143. X#endif
  1144. X{
  1145. X   char *s = Line;
  1146. X   Token tok;
  1147. X   int i;
  1148. X
  1149. X   while (isspace(*s)) s++;
  1150. X
  1151. X   /* Skip comments and blank lines */
  1152. X   if (*s == '#' || *s == 0) {
  1153. X      if (Purge && TopLevel()) Output(Line);
  1154. X      return 0;
  1155. X   }
  1156. X
  1157. X   tok = ParseToken(&s);
  1158. X   switch(tok.type) {
  1159. X      case Push_t:   PushOmitContext();
  1160. X                      if (Purge && TopLevel()) Output(Line);
  1161. X             break;
  1162. X
  1163. X      case Pop_t:    PopOmitContext();
  1164. X                      if (Purge && TopLevel()) Output(Line);
  1165. X             break;
  1166. X
  1167. X      case Clear_t:  ClearOmitContext();
  1168. X                      if (Purge && TopLevel()) Output(Line);
  1169. X             break;
  1170. X
  1171. X      case Banner_t: DoBanner(&s);
  1172. X             if (Purge && TopLevel()) Output(Line);
  1173. X             break;
  1174. X
  1175. X      case Omit_t:   i = DoGlobalOmit(&s);
  1176. X             if (Calendar) return i;
  1177. X             if (Purge && TopLevel())
  1178. X            if (i == -1) Eprint("Purged '%s'\n", Line);
  1179. X            else       Output(Line);
  1180. X             break;
  1181. X
  1182. X      case Rem_t:    i = DoRem(&s);
  1183. X                 if (Calendar) return i;
  1184. X             if (Purge && TopLevel())
  1185. X            if (i < 0) Eprint("Purged '%s'\n", Line);
  1186. X            else       Output(Line);
  1187. X             NumRem++;
  1188. X             break;
  1189. X
  1190. X      case Include_t: if (Purge && TopLevel()) Output(Line);
  1191. X              DoInclude(&s);
  1192. X              break;
  1193. X
  1194. X      default:       if (Purge && TopLevel()) Output(Line);
  1195. X             Eprint("Unknown command '%s'\n", tok.str);
  1196. X   }
  1197. X   return 0;
  1198. X}
  1199. X
  1200. X/***************************************************************/
  1201. X/*                                                             */
  1202. X/*  Standard: void Eprint(const char *f, ...)                  */
  1203. X/*  Unix: void Eprint(va_alist)                                */
  1204. X/*                                                             */
  1205. X/*  Prints an error message.                                   */
  1206. X/*                                                             */
  1207. X/***************************************************************/
  1208. X#ifndef UNIX
  1209. Xvoid Eprint(const char *f, ...)
  1210. X#else
  1211. X/*VARARGS0*/
  1212. Xvoid Eprint(va_alist)
  1213. Xva_dcl
  1214. X#endif
  1215. X{
  1216. X#ifndef UNIX
  1217. X   va_list args;
  1218. X#else
  1219. X  va_list args;
  1220. X  char *f;
  1221. X#endif
  1222. X
  1223. X#ifndef UNIX
  1224. X   if (Verbose & Fresh) {
  1225. X#else
  1226. X  if (Verbose & Fresh) {
  1227. X#endif
  1228. X      fprintf(stderr, "\n--- %s\n", Line);
  1229. X      Fresh = 0;
  1230. X   }
  1231. X   if (Verbose) fprintf(stderr, "--- ");
  1232. X   fprintf(stderr, "%s(%d): ", FileName, CurLine);
  1233. X#ifndef UNIX
  1234. X   va_start(args, f);
  1235. X#else
  1236. X   va_start(args);
  1237. X   f = va_arg(args, char *);
  1238. X#endif
  1239. X   vfprintf(stderr, f, args);
  1240. X#ifdef UNIX
  1241. X   va_end(args);
  1242. X#endif
  1243. X}
  1244. X
  1245. X/***************************************************************/
  1246. X/*                                                             */
  1247. X/*  int DoBanner(char **s)                                     */
  1248. X/*                                                             */
  1249. X/*  Sets the "Reminders for..." banner.                        */
  1250. X/*                                                             */
  1251. X/***************************************************************/
  1252. X#ifdef __STDC__
  1253. Xint DoBanner(char **s)
  1254. X#else
  1255. Xint DoBanner(s)
  1256. X     char **s;
  1257. X#endif
  1258. X{
  1259. X   if (Purge || Next) return 0;
  1260. X   while (isspace(**s)) (*s)++;
  1261. X   strcpy(Banner, *s);
  1262. X   if (! *Banner)
  1263. X   {
  1264. X      if (Debug) Eprint("Empty banner.\n");
  1265. X      strcpy(Banner, "Reminders for %w, %d%s %m, %y%o:");
  1266. X   }
  1267. X   if (NumRem && Debug) Eprint("Warning: Banner after reminder.\n");
  1268. X   return 0;
  1269. X}
  1270. X
  1271. X/***************************************************************/
  1272. X/*                                                             */
  1273. X/* int CheckDate(int d, int m, int y)                          */
  1274. X/*                                                             */
  1275. X/* Checks that a date is valid - returns 0 for OK, 1 for BAD.  */
  1276. X/*                                                             */
  1277. X/* If y=-1, just checks that month & day are valid, giving     */
  1278. X/* benefit of the doubt for February 29.                       */
  1279. X/*                                                             */
  1280. X/* No point in checking if month is valid - months are named   */
  1281. X/* and thus a month out of range can never be entered into     */
  1282. X/* the system.                                                 */
  1283. X/*                                                             */
  1284. X/***************************************************************/
  1285. X#ifdef __STDC__
  1286. Xint CheckDate(int d, int m, int y)
  1287. X#else
  1288. Xint CheckDate(d, m, y)
  1289. X     int d;
  1290. X     int m;
  1291. X     int y;
  1292. X#endif
  1293. X{
  1294. X   if (y == -1)
  1295. X      if (d > 0 && d <= MonthDays[m]) return 0; else return 1;
  1296. X   else
  1297. X      if (y < BASE || y > BASE + 85) return 1;
  1298. X      else if (d > 0 && d <= DaysInMonth(m, y)) return 0; else return 1;
  1299. X}
  1300. X
  1301. X/***************************************************************/
  1302. X/*                                                             */
  1303. X/*  int strncmpi(char *s1, char*s1, int n)                     */
  1304. X/*                                                             */
  1305. X/*  Compares first n chars of string ignoring case.            */
  1306. X/*                                                             */
  1307. X/***************************************************************/
  1308. X#ifdef __STDC__
  1309. Xint strncmpi(char *s1, char *s2, int n)
  1310. X#else
  1311. Xint strncmpi(s1, s2, n)
  1312. X     char *s1;
  1313. X     char *s2;
  1314. X     int n;
  1315. X#endif
  1316. X{
  1317. X   register int u1, u2;
  1318. X   while (n)
  1319. X   {
  1320. X      if (!*s1 || !*s2) return upper(*s1) - upper(*s2);
  1321. X      u1 = upper(*s1);
  1322. X      u2 = upper(*s2);
  1323. X      if (u1 != u2) return (u1 - u2);
  1324. X      n--;
  1325. X      s1++;
  1326. X      s2++;
  1327. X   }
  1328. X   return 0;
  1329. X}
  1330. X
  1331. X/***************************************************************/
  1332. X/*                                                             */
  1333. X/*  ParseToken(char **s);                                      */
  1334. X/*                                                             */
  1335. X/*  Parse the next token and adjust the character pointer.     */
  1336. X/*                                                             */
  1337. X/***************************************************************/
  1338. X#ifdef __STDC__
  1339. XToken ParseToken(char **s)
  1340. X#else
  1341. XToken ParseToken(s)
  1342. X     char **s;
  1343. X#endif
  1344. X{
  1345. X
  1346. X   Token tok;
  1347. X   char *t = TmpBuf;
  1348. X   int i, h, m;
  1349. X   int len;
  1350. X   int top, bot, mid;
  1351. X   char *colon = (char *) NULL;
  1352. X
  1353. X   *t = 0;
  1354. X   tok.str = TmpBuf;
  1355. X
  1356. X   /* Skip blank space */
  1357. X   while (isspace(**s)) (*s)++;
  1358. X
  1359. X   /* End of line ? */
  1360. X   if (**s == 0) {
  1361. X      tok.type = Eol_t;
  1362. X      tok.val = 0;
  1363. X      return tok;
  1364. X   }
  1365. X
  1366. X   /* Grab a space-delimited token */
  1367. X   while (**s != 0 && !isspace(**s)) {
  1368. X      *t++ = **s;
  1369. X      (*s)++;
  1370. X   }
  1371. X   *t = 0;
  1372. X   len = t - TmpBuf;
  1373. X
  1374. X   /* Check if it's a built-in token */
  1375. X   if (*TmpBuf >= 'A' && *TmpBuf <= 'z') {
  1376. X      top = sizeof(keywd)/sizeof(keywd[0])-1;
  1377. X      bot = 0;
  1378. X      mid = (top+bot)/2;
  1379. X      while (top >= bot && 
  1380. X              (i=strncmpi(TmpBuf, keywd[mid].str, MAX(len, (int) keywd[mid].len)))) {
  1381. X         if (i>0) bot = mid+1; else top = mid-1;
  1382. X     mid = (top+bot)/2;
  1383. X      }
  1384. X      if (top >= bot) return keywd[mid];
  1385. X   }
  1386. X
  1387. X   tok.type = Unknown_t;
  1388. X
  1389. X   /* If it's a comment, ignore the rest of the line */
  1390. X   if (*(tok.str) == '#') {
  1391. X      tok.type = Eol_t;
  1392. X      return tok;
  1393. X   }
  1394. X
  1395. X   /* Check if it's a number (optional + / - / * ahead of number */
  1396. X   t = TmpBuf;
  1397. X   i = 1;  /* multiplication factor */
  1398. X   if (isdigit(*t)) {
  1399. X      while (*++t){
  1400. X         if (*t == ':') {
  1401. X            if (colon) return tok; else colon = t;
  1402. X         } else if (!isdigit(*t)) return tok;
  1403. X      }
  1404. X   }
  1405. X   else if (*t == '+' || *t == '-' || *t == '*') {
  1406. X      /* Check if it's a "++" or a "--" */
  1407. X      if ((*t == '+' && *(t+1) == '+') || (*t == '-' && *(t+1) == '-')) {
  1408. X         i = -1;
  1409. X         t++;
  1410. X      }
  1411. X      if (!isdigit(*++t)) return tok;
  1412. X      while (*++t) if (!isdigit(*t)) return tok;
  1413. X   }
  1414. X   else return tok;
  1415. X
  1416. X   /* OK, here we have a number - either a pure number, a delta, a time,
  1417. X      back or a repeat marker */
  1418. X
  1419. X   if (colon) { /* got a time here */
  1420. X      h = atoi(TmpBuf);
  1421. X      m = atoi(colon + 1);
  1422. X      if (h >= 0 && h <= 23 && m >= 0 && m <= 59) {
  1423. X         tok.type = Time_t;
  1424. X     tok.val = 60*h+m;
  1425. X      }
  1426. X      else return tok;
  1427. X   }
  1428. X   else if (*TmpBuf == '+') {
  1429. X      tok.type = Delta_t;
  1430. X      if (i == 1) tok.val = atoi(TmpBuf + 1);
  1431. X      else        tok.val = -atoi(TmpBuf + 2);
  1432. X   }
  1433. X   else if (*TmpBuf == '-') {
  1434. X      tok.type = Back_t;
  1435. X      if (i == 1) tok.val = atoi(TmpBuf + 1);
  1436. X      else        tok.val = -atoi(TmpBuf + 2);
  1437. X   }
  1438. X   else if (*TmpBuf == '*') {
  1439. X      tok.type = Repeat_t;
  1440. X      tok.val = atoi(TmpBuf + 1);
  1441. X   }
  1442. X   else {
  1443. X      tok.val = atoi(TmpBuf);
  1444. X      if (tok.val > 0 && tok.val <= 31) tok.type = Day_t;
  1445. X      else if (tok.val >= 100) tok.type = Year_t;
  1446. X      else {
  1447. X     tok.type = Year_t;
  1448. X     tok.val += 1900;
  1449. X      }
  1450. X   }
  1451. X   return tok;
  1452. X}
  1453. X
  1454. X/***************************************************************/
  1455. X/*                                                             */
  1456. X/*  int FromJulian(int jul, int *d, int *m, int *y)            */
  1457. X/*                                                             */
  1458. X/*  Convert a date from Julian to normal form.  Returns        */
  1459. X/*  0 if conversion ok, -1 otherwise.                          */
  1460. X/*                                                             */
  1461. X/***************************************************************/
  1462. X#ifdef __STDC__
  1463. Xint FromJulian(int jul, int *d, int *m, int *y)
  1464. X#else
  1465. Xint FromJulian(jul, d, m, y)
  1466. X     int jul;
  1467. X     int *d;
  1468. X     int *m;
  1469. X     int *y;
  1470. X#endif
  1471. X{
  1472. X   int t;
  1473. X
  1474. X   if (jul < 0) return -1;
  1475. X
  1476. X   if (jul >= JulFirst && JulFirst != -1) {
  1477. X      *y = FirstYear;
  1478. X      jul -= JulFirst;
  1479. X   } else *y = BASE;
  1480. X
  1481. X   *m = 0;
  1482. X
  1483. X   t = DaysInYear(*y);
  1484. X   while (jul >= t) {
  1485. X      jul -= t;
  1486. X      (*y)++;
  1487. X      t = DaysInYear(*y);
  1488. X   }
  1489. X
  1490. X   t = DaysInMonth(*m, *y);
  1491. X   while (jul >= t) {
  1492. X      jul -= t;
  1493. X      (*m)++;
  1494. X      t = DaysInMonth(*m, *y);
  1495. X   }
  1496. X   *d = jul + 1;
  1497. X   return 0;
  1498. X}
  1499. X
  1500. X/***************************************************************/
  1501. X/*                                                             */
  1502. X/*  int Julian(d, m, y)                                        */
  1503. X/*                                                             */
  1504. X/*  Converts a date to the number of days after Jan 1 1990.    */
  1505. X/*  Returns -1 if date is before Jan 1 1990.                   */
  1506. X/*                                                             */
  1507. X/***************************************************************/
  1508. X#ifdef __STDC__
  1509. Xint Julian(int d, int m, int y)
  1510. X#else
  1511. Xint Julian(d, m, y)
  1512. X     int d;
  1513. X     int m;
  1514. X     int y;
  1515. X#endif
  1516. X{
  1517. X   int iy;
  1518. X   int jul = 0;
  1519. X
  1520. X   if (y < BASE) return -1;
  1521. X   if (JulFirst == -1 || y < FirstYear)
  1522. X      for (iy = BASE; iy < y; iy++) jul += DaysInYear(iy);
  1523. X   else {
  1524. X      jul = JulFirst;
  1525. X      for (iy = FirstYear; iy < y; iy++) jul += DaysInYear(iy);
  1526. X   }
  1527. X
  1528. X   return jul + MonthIndex[IsLeapYear(y)][m] + d - 1;
  1529. X}
  1530. X
  1531. X/***************************************************************/
  1532. X/*                                                             */
  1533. X/*  int FindTodaysDate(int *d, int *m, int *y)                 */
  1534. X/*                                                             */
  1535. X/*  Obtains today's date.  Returns Julian date or -1 for       */
  1536. X/*  failure.                                                   */
  1537. X/*                                                             */
  1538. X/***************************************************************/
  1539. X#ifdef __STDC__
  1540. Xint FindTodaysDate(int *d, int *m, int *y)
  1541. X#else
  1542. Xint FindTodaysDate(d, m, y)
  1543. X     int *d;
  1544. X     int *m;
  1545. X     int *y;
  1546. X#endif
  1547. X{
  1548. X#ifndef UNIX
  1549. X   struct dosdate_t buf;
  1550. X
  1551. X   _dos_getdate(&buf);
  1552. X
  1553. X   *d = buf.day;
  1554. X   *m = buf.month - 1;
  1555. X   *y = buf.year;
  1556. X#else
  1557. X  time_t tloc;
  1558. X  struct tm *t;
  1559. X
  1560. X   (void) time(&tloc);
  1561. X   t = localtime(&tloc);
  1562. X
  1563. X   *d = t->tm_mday;
  1564. X   *m = t->tm_mon;
  1565. X   *y = t->tm_year + 1900;
  1566. X
  1567. X#endif
  1568. X   if (CheckDate(*d, *m, *y)) return -1;
  1569. X   return Julian(*d, *m, *y);
  1570. X}
  1571. X/***************************************************************/
  1572. X/***************************************************************/
  1573. X/**                                                           **/
  1574. X/** MAIN PROGRAM ENTRY POINT                                  **/
  1575. X/**                                                           **/
  1576. X/***************************************************************/
  1577. X/***************************************************************/
  1578. X#ifdef __STDC__
  1579. Xint main(int argc, char *argv[])
  1580. X#else
  1581. Xint main(argc, argv)
  1582. X     int argc;
  1583. X     char *argv[];
  1584. X#endif
  1585. X{
  1586. X#ifdef UNIX
  1587. X#ifdef SYSV
  1588. X   pid_t pid;
  1589. X#else
  1590. X   int pid;
  1591. X#endif
  1592. X#endif
  1593. X
  1594. X   NumEmitted = 0;
  1595. X   NumRem = 0;
  1596. X   JulFirst = -1;  /* Initialize JulFirst so it's not used by Julian */
  1597. X      
  1598. X   JulianToday = FindTodaysDate(&CurDay, &CurMon, &CurYear);
  1599. X   if (JulianToday < 0) {
  1600. X      fprintf(stderr, "remind: System date is illegal - Ensure that year is at least %d.\n", BASE);
  1601. X      return 1;
  1602. X   }
  1603. X   
  1604. X   RealToday = JulianToday;
  1605. X   
  1606. X   initialize(argc, argv);
  1607. X
  1608. X   FirstYear = CurYear;
  1609. X   JulFirst = Julian(1, 0, CurYear);  /* Do expensive computation once */
  1610. X   FirstYear = CurYear;
  1611. X
  1612. X   if (Calendar) {
  1613. X      DoCalendar();
  1614. X      return 0;
  1615. X   }
  1616. X   while (1) {
  1617. X      if (ReadLine()) break;
  1618. X      ProcessLine();
  1619. X   }
  1620. X/* Get rid of any spurious OMIT contexts */
  1621. X   FreeStackedOmits();
  1622. X   if (NumEmitted == 0 && NumAtsQueued == 0 && !Purge && !Debug && !Next) 
  1623. X       printf("No reminders.\n");
  1624. X#ifdef UNIX
  1625. X   if (NumEmitted == 0 && NumAtsQueued != 0 && !Purge && !Debug)
  1626. X       printf("%d reminder%s queued for later today.\n", NumAtsQueued,
  1627. X              (NumAtsQueued == 1) ? "" : "s");
  1628. X
  1629. X   fflush(stdout); /* Flush output so we don't get 2 copies when directing */
  1630. X           /* stdout to a file. */
  1631. X
  1632. X   if (NumAtsQueued) {
  1633. X      pid = fork();
  1634. X      if (pid == -1) Eprint("Can't fork to perform ATs!\n");
  1635. X      if (pid != 0) return 0;
  1636. X      HandleQueuedAts();
  1637. X   }
  1638. X#endif
  1639. X   return 0;
  1640. X}
  1641. X/***************************************************************/
  1642. X/*                                                             */
  1643. X/* SystemTime                                                  */
  1644. X/*                                                             */
  1645. X/* Returns current system time in seconds past midnight.       */
  1646. X/*                                                             */
  1647. X/***************************************************************/
  1648. X#ifdef __STDC__
  1649. Xlong SystemTime(void)
  1650. X#else
  1651. Xlong SystemTime()
  1652. X#endif
  1653. X{
  1654. X#ifdef UNIX
  1655. X  time_t tloc;
  1656. X  struct tm *t;
  1657. X
  1658. X   (void) time(&tloc);
  1659. X   t = localtime(&tloc);
  1660. X   return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L + (long) t->tm_sec;
  1661. X
  1662. X#else
  1663. X   struct dostime_t tloc;
  1664. X   _dos_gettime(&tloc);
  1665. X   return (long) tloc.hour * 3600L + (long) tloc.minute * 60L + (long) tloc.second;
  1666. X#endif
  1667. X}
  1668. END_OF_FILE
  1669. if test 20411 -ne `wc -c <'main.c'`; then
  1670.     echo shar: \"'main.c'\" unpacked with wrong size!
  1671. fi
  1672. # end of 'main.c'
  1673. fi
  1674. if test -f 'test.out' -a "${1}" != "-c" ; then 
  1675.   echo shar: Will not clobber existing file \"'test.out'\"
  1676. else
  1677. echo shar: Extracting \"'test.out'\" \(6464 characters\)
  1678. sed "s/^X//" >'test.out' <<'END_OF_FILE'
  1679. X
  1680. XFile test.rem last accessed on Tuesday, 19 February, 1991
  1681. X
  1682. X--- REM MSG Every Day
  1683. X--- test.rem(19): *Trigger date: Saturday, 16 February, 1991.
  1684. X
  1685. X--- REM 18 MSG Every 18th 
  1686. X--- test.rem(21): Trigger date: Monday, 18 February, 1991.
  1687. X
  1688. X--- REM 15 MSG Every 15th
  1689. X--- test.rem(22): Trigger date: Friday, 15 March, 1991.
  1690. X
  1691. X--- REM Feb MSG February
  1692. X--- test.rem(24): *Trigger date: Saturday, 16 February, 1991.
  1693. X
  1694. X--- REM Jan MSG January
  1695. X--- test.rem(25): Trigger date: Wednesday, 1 January, 1992.
  1696. X
  1697. X--- REM March MSG March
  1698. X--- test.rem(26): Trigger date: Friday, 1 March, 1991.
  1699. X
  1700. X--- REM 13 Jan MSG 13 Jan
  1701. X--- test.rem(28): Trigger date: Monday, 13 January, 1992.
  1702. X
  1703. X--- REM 15 Feb MSG 15 Feb
  1704. X--- test.rem(29): Trigger date: Saturday, 15 February, 1992.
  1705. X
  1706. X--- REM 28 Feb MSG 28 Feb
  1707. X--- test.rem(30): Trigger date: Thursday, 28 February, 1991.
  1708. X
  1709. X--- REM 29 Feb MSG 29 Feb
  1710. X--- test.rem(31): Trigger date: Saturday, 29 February, 1992.
  1711. X
  1712. X--- REM 5 Mar MSG 5 Mar
  1713. X--- test.rem(32): Trigger date: Tuesday, 5 March, 1991.
  1714. X
  1715. X--- REM 1990 MSG 1990
  1716. X--- test.rem(34): Reminder has expired.
  1717. X
  1718. X--- REM 1991 MSG 1991
  1719. X--- test.rem(35): *Trigger date: Saturday, 16 February, 1991.
  1720. X
  1721. X--- REM 1992 MSG 1991
  1722. X--- test.rem(36): Trigger date: Wednesday, 1 January, 1992.
  1723. X
  1724. X--- REM 1 1990 MSG 1 1990
  1725. X--- test.rem(38): Reminder has expired.
  1726. X
  1727. X--- REM 29 1991 MSG 29 1991
  1728. X--- test.rem(39): Trigger date: Friday, 29 March, 1991.
  1729. X
  1730. X--- REM 29 1992 MSG 29 1992
  1731. X--- test.rem(40): Trigger date: Wednesday, 29 January, 1992.
  1732. X
  1733. X--- REM 16 1991 MSG 16 1991
  1734. X--- test.rem(41): *Trigger date: Saturday, 16 February, 1991.
  1735. X
  1736. X--- REM Jan 1990 MSG Jan 1990
  1737. X--- test.rem(43): Reminder has expired.
  1738. X
  1739. X--- REM Feb 1991 MSG Feb 1991
  1740. X--- test.rem(44): *Trigger date: Saturday, 16 February, 1991.
  1741. X
  1742. X--- REM Dec 1991 MSG Dec 1991
  1743. X--- test.rem(45): Trigger date: Sunday, 1 December, 1991.
  1744. X
  1745. X--- REM May 1992 MSG May 1992
  1746. X--- test.rem(46): Trigger date: Friday, 1 May, 1992.
  1747. X
  1748. X--- REM 1 Jan 1991 MSG 1 Jan 1991
  1749. X--- test.rem(48): Reminder has expired.
  1750. X
  1751. X--- REM 16 Feb 1991 MSG 16 Feb 1991
  1752. X--- test.rem(49): *Trigger date: Saturday, 16 February, 1991.
  1753. X
  1754. X--- REM 29 Dec 1992 MSG 29 Dec 1992
  1755. X--- test.rem(50): Trigger date: Tuesday, 29 December, 1992.
  1756. X
  1757. X--- REM Sun MSG Sun
  1758. X--- test.rem(52): Trigger date: Sunday, 17 February, 1991.
  1759. X
  1760. X--- REM Fri Sat Tue MSG Fri Sat Tue
  1761. X--- test.rem(53): *Trigger date: Saturday, 16 February, 1991.
  1762. X
  1763. X--- REM Sun 16 MSG Sun 16
  1764. X--- test.rem(55): Trigger date: Sunday, 17 February, 1991.
  1765. X
  1766. X--- REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1
  1767. X--- test.rem(56): Trigger date: Friday, 1 March, 1991.
  1768. X
  1769. X--- REM Sun Feb MSG Sun Feb
  1770. X--- test.rem(58): Trigger date: Sunday, 17 February, 1991.
  1771. X
  1772. X--- REM Mon Tue March MSG Mon Tue March
  1773. X--- test.rem(59): Trigger date: Monday, 4 March, 1991.
  1774. X
  1775. X--- REM Sun 16 Feb MSG Sun 16 Feb
  1776. X--- test.rem(61): Trigger date: Sunday, 17 February, 1991.
  1777. X
  1778. X--- REM Mon Tue 10 March MSG Mon Tue 10 March
  1779. X--- test.rem(62): Trigger date: Monday, 11 March, 1991.
  1780. X
  1781. X--- REM Sat Sun 1991 MSG Sat Sun 1991
  1782. X--- test.rem(64): *Trigger date: Saturday, 16 February, 1991.
  1783. X
  1784. X--- REM Mon Tue 1992 MSG Mon Tue 1992
  1785. X--- test.rem(65): Trigger date: Monday, 6 January, 1992.
  1786. X
  1787. X--- REM Sun 16 1991 MSG Sun 16 1991
  1788. X--- test.rem(67): Trigger date: Sunday, 17 February, 1991.
  1789. X
  1790. X--- REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992
  1791. X--- test.rem(68): Trigger date: Wednesday, 1 January, 1992.
  1792. X
  1793. X--- REM Mon Feb 1991 MSG Mon Feb 1991
  1794. X--- test.rem(70): Trigger date: Monday, 18 February, 1991.
  1795. X
  1796. X--- REM Tue Jan 1992 MSG Tue Jan 1992
  1797. X--- test.rem(71): Trigger date: Tuesday, 7 January, 1992.
  1798. X
  1799. X--- REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991
  1800. X--- test.rem(73): Trigger date: Sunday, 17 February, 1991.
  1801. X
  1802. X--- REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992
  1803. X--- test.rem(74): Trigger date: Tuesday, 28 January, 1992.
  1804. X
  1805. X--- REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun
  1806. X--- test.rem(78): Trigger date: Thursday, 28 February, 1991.
  1807. X
  1808. X--- REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun
  1809. X--- test.rem(79): Trigger date: Thursday, 28 February, 1991.
  1810. X
  1811. X--- REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted)
  1812. X--- test.rem(82): Trigger date: Wednesday, 27 February, 1991.
  1813. X
  1814. X--- REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted)
  1815. X--- test.rem(83): Trigger date: Thursday, 28 February, 1991.
  1816. X
  1817. X--- REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991
  1818. X--- test.rem(88): Trigger date: Wednesday, 20 February, 1991.
  1819. X
  1820. X--- REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
  1821. X--- test.rem(93): Trigger date: Wednesday, 27 February, 1991.
  1822. X
  1823. X--- REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
  1824. X--- test.rem(94): Trigger date: Thursday, 28 February, 1991.
  1825. X
  1826. X--- REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
  1827. X--- test.rem(95): Trigger date: Wednesday, 27 February, 1991.
  1828. X
  1829. X--- REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
  1830. X--- test.rem(96): Trigger date: Friday, 28 February, 1992.
  1831. X
  1832. X--- REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
  1833. X--- test.rem(97): Trigger date: Friday, 1 March, 1991.
  1834. X
  1835. X--- REM 1 Mar -1 MSG 1 mar -1
  1836. X--- test.rem(101): Trigger date: Thursday, 28 February, 1991.
  1837. X
  1838. X--- REM 1 Mar --1 MSG 1 mar --1
  1839. X--- test.rem(102): Trigger date: Thursday, 28 February, 1991.
  1840. X
  1841. X--- REM 28 Feb BEFORE MSG 28 Feb BEFORE
  1842. X--- test.rem(103): Trigger date: Thursday, 28 February, 1991.
  1843. X
  1844. X--- REM 28 Feb SKIP MSG 28 Feb SKIP 
  1845. X--- test.rem(104): Trigger date: Thursday, 28 February, 1991.
  1846. X
  1847. X--- REM 28 Feb AFTER MSG 28 Feb AFTER
  1848. X--- test.rem(105): Trigger date: Thursday, 28 February, 1991.
  1849. X
  1850. X--- REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
  1851. X--- test.rem(108): Trigger date: Wednesday, 27 February, 1991.
  1852. X
  1853. X--- REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
  1854. X--- test.rem(109): Trigger date: Thursday, 28 February, 1991.
  1855. X
  1856. X--- REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
  1857. X--- test.rem(110): Trigger date: Wednesday, 27 February, 1991.
  1858. X
  1859. X--- REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
  1860. X--- test.rem(111): Trigger date: Friday, 28 February, 1992.
  1861. X
  1862. X--- REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
  1863. X--- test.rem(112): Trigger date: Friday, 1 March, 1991.
  1864. X
  1865. X--- REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91
  1866. X--- test.rem(115): Trigger date: Wednesday, 13 March, 1991.
  1867. X
  1868. X--- REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
  1869. X--- test.rem(119): Trigger date: Monday, 18 February, 1991.
  1870. X
  1871. X--- REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted)
  1872. X--- test.rem(122): *Trigger date: Monday, 18 February, 1991.
  1873. X
  1874. X--- REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted)
  1875. X--- test.rem(123): Trigger date: Monday, 18 February, 1991.
  1876. END_OF_FILE
  1877. if test 6464 -ne `wc -c <'test.out'`; then
  1878.     echo shar: \"'test.out'\" unpacked with wrong size!
  1879. fi
  1880. # end of 'test.out'
  1881. fi
  1882. echo shar: End of archive 2 \(of 4\).
  1883. cp /dev/null ark2isdone
  1884. MISSING=""
  1885. for I in 1 2 3 4 ; do
  1886.     if test ! -f ark${I}isdone ; then
  1887.     MISSING="${MISSING} ${I}"
  1888.     fi
  1889. done
  1890. if test "${MISSING}" = "" ; then
  1891.     echo You have unpacked all 4 archives.
  1892.     rm -f ark[1-9]isdone
  1893. else
  1894.     echo You still need to unpack the following archives:
  1895.     echo "        " ${MISSING}
  1896. fi
  1897. ##  End of shell archive.
  1898. exit 0
  1899. exit 0 # Just in case...
  1900. -- 
  1901. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1902. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1903. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1904. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1905.